package ${projectDomain}.filter;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.nacos.common.utils.StringUtils;
import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import ${projectDomain}.pojo.mso.logger.api.request.LoggerApiRequestQueueMso;
import ${projectDomain}.pojo.vo.ResponseBody;
import ${projectDomain}.rabbit.RabbitConstant;
import ${projectDomain}.rabbit.RabbitQueue;
import ${projectDomain}.response.UnswiftResponse;
import com.unswift.exception.CoreException;
import com.unswift.utils.ExceptionUtils;
import com.unswift.utils.JsonUtils;
import com.unswift.utils.ObjectUtils;

import io.netty.buffer.ByteBufAllocator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Component
@Api(value="接口请求日志拦截器", author = "unswift", date = "2023-12-08", version = "1.0.0")
public class LoggerFilter extends CloudFilter implements GlobalFilter, Ordered, RabbitConstant{

	@ApiField("日志对象")
	public final Logger logger=LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	@ApiField("Rabbit Mq发送消息服务")
	private RabbitQueue rabbitQueue;
	
	@Override
	@ApiMethod(value = "获取filter的执行顺序，值越小，越靠前，此值最好不要大于1，否则的话取到的path会没有路由", returns = @ApiField("执行顺序"))
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE+2;
	}

	@Override
	@ApiMethod(value = "拦截器执行方法，在此方法中拦截签名", params = {@ApiField( "http请求的封装，提供对HTTP的访问请求和响应"), @ApiField("委托" )})
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		LoggerApiRequestQueueMso apiLogger=new LoggerApiRequestQueueMso();
		ServerHttpRequest request = exchange.getRequest();
		apiLogger.setPath(request.getPath().value());
		try {
			HttpHeaders headers = request.getHeaders();
			String token=headers.getFirst(tokenConfig.getKeyName());
			apiLogger.setToken(token);
			apiLogger.setHeaders(JsonUtils.toJson(headers));
			MultiValueMap<String, String> params = request.getQueryParams();
			if(ObjectUtils.isNotEmpty(params)) {
				apiLogger.setParams(JsonUtils.toJson(params));
			}
			String contentType=headers.getFirst(HttpHeaders.CONTENT_TYPE);
			byte[] bytes;
			if(StringUtils.startsWith(contentType, "multipart/form-data;")) {
				bytes=this.getRequstByteBody(request);
				if(ObjectUtils.isNotEmpty(bytes)) {
					apiLogger.setBodys(new String(bytes, StandardCharsets.UTF_8));
				}
			}else {
				String body=this.getRequstBody(request);
				if(ObjectUtils.isNotEmpty(body)) {
					apiLogger.setBodys(body);
				}
				bytes=ObjectUtils.init(body, "").getBytes(StandardCharsets.UTF_8);
			}
			
			NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
			DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
			buffer.write(bytes);
			Flux<DataBuffer> bodyFlux = Flux.just(buffer);
			ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
				@Override
				public Flux<DataBuffer> getBody() {
					return bodyFlux;
				}
			};
			ServerHttpResponse originalResponse = exchange.getResponse();
			DataBufferFactory bufferFactory = originalResponse.bufferFactory();
			ServerHttpResponseDecorator response = new UnswiftResponse(originalResponse, apiLogger) {
	            @Override
	            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
	                if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) {
	                	// 获取ContentType，判断是否返回JSON格式数据
	                    String originalResponseContentType = exchange.getAttribute("original_response_content_type");
	                    if (ObjectUtils.isNotEmpty(originalResponseContentType) && originalResponseContentType.contains("application/json")) {
	                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
	                        //（返回数据内如果字符串过大，默认会切割）解决返回体分段传输
	                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
	                        	LoggerResponseEncapsulation response=new LoggerResponseEncapsulation();
	                            dataBuffers.forEach(dataBuffer -> {
	                                try {
	                                    byte[] outputData = new byte[dataBuffer.readableByteCount()];
	                                    dataBuffer.read(outputData);
	                                    DataBufferUtils.release(dataBuffer);
	                                    response.addByteArray(outputData);
	                                } catch (Exception e) {
	                                	e.printStackTrace();
	                                	logger.error(e.getMessage(), e);
	                                	this.getApiLogger().setResponse(ExceptionUtils.getStackTrace(e));
	                                }
	                            });
	                            byte[] buffer=response.getByteArray();
	                            this.getApiLogger().setResponse(new String(buffer));
	                            rabbitQueue.sendMessage(LOGGER_API_REQUEST_EXCHANGE_NAME, LOGGER_API_REQUEST_ROUTE_KEY, this.getApiLogger());//发送导出队列
	                            //byte[] uppedContent = new String(responseData.getBytes(), Charset.forName("UTF-8")).getBytes();
	                            originalResponse.getHeaders().setContentLength(buffer.length);
	                            return bufferFactory.wrap(buffer);
	                        }));
	                    }
	                }
	                return super.writeWith(body);
	            }

	            @Override
	            public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
	                return writeWith(Flux.from(body).flatMapSequential(p -> p));
	            }
	        };
			return chain.filter(exchange.mutate().request(mutatedRequest).response(response).build());
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage(), e);
			apiLogger.setResponse(ExceptionUtils.getStackTrace(e));
			rabbitQueue.sendMessage(LOGGER_API_REQUEST_EXCHANGE_NAME, LOGGER_API_REQUEST_ROUTE_KEY, apiLogger);//发送导出队列
			CoreException exception = ExceptionUtils.message("gateway.handler.exception", e.getMessage());
			return this.toJson(exchange, ResponseBody.error(exception.getCode(), exception.getMessage()));
		}
	}
	
	@Api(value="回调方法封装")
	public static class LoggerResponseEncapsulation{
		
		@ApiField("回调数据列表")
		private List<byte[]> list;
		
		@ApiField("回调数据总长度")
		private Integer length;
		
		private void addLength(int add) {
			if(ObjectUtils.isEmpty(length)) {
				length=0;
			}
			this.length+=add;
		}
		public Integer getLength() {
			return length;
		}
		public void addByteArray(byte[] data) {
			if(ObjectUtils.isEmpty(list)) {
				list=new ArrayList<byte[]>();
			}
			list.add(data);
			this.addLength(data.length);
		}
		public List<byte[]> getList() {
			return list;
		}
		public byte[] getByteArray() {
			byte[] buffer=new byte[length];
            int index=0, subLength;
            for (byte[] subBuff : list) {
            	subLength = subBuff.length;
            	System.arraycopy(subBuff, 0, buffer, index, subLength);
				index+=subLength;
			}
            return buffer;
		}
	}
}
